﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace Xceed.Silverlight.DataGrid
{
  internal static class LayoutTransitionHelper
  {
    private static double CachedTranslationDistance = 70d;

    private static bool IsAlmostEqual( double a, double b )
    {
      return ( Math.Abs( ( double )( a - b ) ) < 1E-07 );
    }

    public static void InitializeTransition( IAnimatedLayoutElement element, TransitionContext arrangeContext )
    {
      Debug.Assert( element.AnimationX == 0 );
      Debug.Assert( element.AnimationY == 0 );
      Debug.Assert( element.Projection == null );
      Debug.Assert( element.Opacity == 1d );
      Debug.Assert( double.IsNaN( element.Height ) );
      Debug.Assert( double.IsNaN( element.Width ) );
    }

    public static void EndTransition( IAnimatedLayoutElement element )
    {
      element.AnimationX = 0;
      element.AnimationY = 0;
      element.Projection = null;
      element.Height = double.NaN;
      element.Width = double.NaN;
      element.Opacity = 1.0;
      element.ClearBitmapCache();
    }

    public static void ArrangeAnimation( IAnimatedLayoutElement element, TransitionContext transitionContext, Rect currentRect )
    {
      LayoutTransitionHelper.ArrangeAnimation( element, transitionContext, currentRect, Orientation.Vertical );
      LayoutTransitionHelper.ArrangeAnimation( element, transitionContext, currentRect, Orientation.Horizontal );
    }

    private static void ArrangeAnimation( IAnimatedLayoutElement element, TransitionContext transitionContext, Rect currentRect, Orientation orientation )
    {
      if( element.LastArrangeRect != null && transitionContext != null && transitionContext.Storyboard != null )
      {
        Storyboard storyboard = transitionContext.Storyboard;
        Rect previousLayout = element.LastArrangeRect.Value;

        string axis;
        string dimension;
        LayoutRange previousRange = previousLayout.GetOrientedRange( orientation );
        LayoutRange currentRange = currentRect.GetOrientedRange( orientation );

        if( orientation == Orientation.Vertical )
        {
          axis = "Y";
          dimension = "Height";
        }
        else
        {
          axis = "X";
          dimension = "Width";
        }

        bool needTranslate = !LayoutTransitionHelper.IsAlmostEqual( previousRange.Offset, currentRange.Offset );
        bool needResize = !LayoutTransitionHelper.IsAlmostEqual( previousRange.Length, currentRange.Length );
        if( needTranslate )
        {
          DoubleAnimation TranslateAnim = LayoutTransitionHelper.CreatePositionAnimation( transitionContext, element, previousRange.Offset, currentRange.Offset, axis );
          element.SetOrientedAnimationPosition( TranslateAnim.From.Value, orientation );
          storyboard.Children.Add( TranslateAnim );

          if( !needResize && Math.Abs( currentRange.Offset - previousRange.Offset ) > LayoutTransitionHelper.CachedTranslationDistance )
          {
            element.PrepareBitmapCache();
          }
        }

        if( needResize)
        {
          DoubleAnimation lengthAnimation = LayoutTransitionHelper.CreateSizeAnimation( transitionContext, element, previousRange.Length, currentRange.Length, dimension );
          element.SetOrientedLength( lengthAnimation.From.Value, orientation );
          storyboard.Children.Add( lengthAnimation );
        }
      }
    }

    private static DoubleAnimation CreatePositionAnimation( TransitionContext context, ILayoutElement element, double previousPosition, double currentPosition, string axis )
    {
      DoubleAnimation positionAnimation = null;
      double positionDelta = previousPosition - currentPosition;
      positionAnimation = new DoubleAnimation();
      positionAnimation.Duration = context.Duration;
      positionAnimation.EasingFunction = context.EasingFunction;
      positionAnimation.From = positionDelta;
      positionAnimation.To = 0.0;
      positionAnimation.FillBehavior = FillBehavior.HoldEnd;
      //Debug.WriteLine( "Position for={0} axis={1}, from={2}, to={3}", element, axis, positionDelta, 0 );


      Storyboard.SetTarget( positionAnimation, ( DependencyObject )element );
      Storyboard.SetTargetProperty( positionAnimation, new PropertyPath( "Animation"+ axis, new object[ 0 ] ) );

      return positionAnimation;
    }

    private static DoubleAnimation CreateSizeAnimation( TransitionContext context, ILayoutElement element, double previousSize, double currentSize, string propertyName )
    {
      DoubleAnimation sizeAnimation = null;

      sizeAnimation = new DoubleAnimation();
      sizeAnimation.Duration = context.Duration;
      sizeAnimation.EasingFunction = context.EasingFunction;
      sizeAnimation.From = previousSize;
      sizeAnimation.To = currentSize;
      sizeAnimation.FillBehavior = FillBehavior.HoldEnd;
      Storyboard.SetTarget( sizeAnimation, ( DependencyObject )element );
      Storyboard.SetTargetProperty( sizeAnimation, new PropertyPath( propertyName, new object[ 0 ] ) );

      return sizeAnimation;
    }

    private static DoubleAnimation CreateOpacityAnimation( TransitionContext context, DependencyObject element )
    {
      DoubleAnimation opacityAnimation = new DoubleAnimation();
      opacityAnimation.Duration = context.Duration;
      opacityAnimation.EasingFunction = context.EasingFunction;
      Storyboard.SetTargetProperty( opacityAnimation, new PropertyPath( "Opacity", new object[ 0 ] ) );
      Storyboard.SetTarget( opacityAnimation, element );
      return opacityAnimation;
    }


    public static void SetAddAnimationForItem( TransitionContext context, IAnimatedLayoutElement element )
    {
      Timeline addAnimation = LayoutTransitionHelper.CreateAddAnimationsForItem( context, element, Orientation.Vertical );
      context.Storyboard.Children.Add( addAnimation );
    }

    public static void SetRemoveAnimationForItem( TransitionContext context, IAnimatedLayoutElement element )
    {
      Timeline addAnimation = LayoutTransitionHelper.CreateRemoveAnimationsForItem( context, element, Orientation.Vertical );
      context.Storyboard.Children.Add( addAnimation );
    }

    public static Timeline CreateAddAnimationsForItem( TransitionContext context, IAnimatedLayoutElement element, Orientation orientation )
    {

      DoubleAnimation addAnimation = LayoutTransitionHelper.CreateOpacityAnimation( context, ( DependencyObject )element );
      addAnimation.From = 0d;
      addAnimation.To = 1d;
      return addAnimation;
    }

    public static Timeline CreateRemoveAnimationsForItem( TransitionContext context, IAnimatedLayoutElement element, Orientation orientation )
    {

      DoubleAnimation removeAnimation = LayoutTransitionHelper.CreateOpacityAnimation( context, ( DependencyObject )element );
      removeAnimation.From = 1d;
      removeAnimation.To = 0d;
      return removeAnimation;
    }
  }

}
